package com.terana.lbs.marketing.web.service;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.github.pagehelper.PageInfo;
import com.tarena.lbs.attach.api.AttachApi;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.BasePageList;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.basic.api.BasicApi;
import com.tarena.lbs.basic.api.UserApi;
import com.tarena.lbs.common.passport.enums.Roles;
import com.tarena.lbs.common.passport.principle.UserPrinciple;
import com.tarena.lbs.common.security.utils.LbsSecurityContenxt;
import com.tarena.lbs.pojo.attach.dto.AttachQrDTO;
import com.tarena.lbs.pojo.attach.param.AttachQRParam;
import com.tarena.lbs.pojo.basic.dto.AdminDTO;
import com.tarena.lbs.pojo.marketing.bo.CouponBO;
import com.tarena.lbs.pojo.marketing.bo.CouponCodeBO;
import com.tarena.lbs.pojo.marketing.bo.MarketingActivityBO;
import com.tarena.lbs.pojo.marketing.bo.UserReceiveCouponBO;
import com.tarena.lbs.pojo.marketing.param.CouponCodeParam;
import com.tarena.lbs.pojo.marketing.param.CouponParam;
import com.tarena.lbs.pojo.marketing.param.UserCouponsParam;
import com.tarena.lbs.pojo.marketing.po.CouponCodePO;
import com.tarena.lbs.pojo.marketing.po.CouponPO;
import com.tarena.lbs.pojo.marketing.query.CouponQuery;
import com.tarena.lbs.pojo.marketing.query.UserCouponCodeQuery;
import com.tarena.lbs.pojo.marketing.query.UserCouponQuery;
import com.tarena.lbs.pojo.marketing.vo.CouponVO;
import com.tarena.lbs.pojo.stock.param.CouponStockParam;
import com.tarena.lbs.stock.api.StockApi;
import com.terana.lbs.marketing.web.repository.ActivityRepository;
import com.terana.lbs.marketing.web.repository.CouponCodeRepository;
import com.terana.lbs.marketing.web.repository.CouponRepository;
import com.terana.lbs.marketing.web.repository.UserReceiveCouponRepo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CouponService {
    @Autowired
    private CouponRepository couponRepository;
    @Autowired
    private CouponCodeRepository couponCodeRepository;
    @Autowired
    private ActivityRepository activityRepository;
    @Autowired
    private UserReceiveCouponRepo userReceiveCouponRepo;
    @Autowired
    private BasicApi basicApi;
    @Autowired
    private StockApi stockApi;
    @Autowired
    private AttachApi attachApi;
    @Autowired
    private UserApi userApi;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public PageResult<CouponVO> couponPage(CouponQuery query) throws BusinessException {
        //1.获取认证对象 判断存在
        UserPrinciple userPrinciple = LbsSecurityContenxt.getLoginToken();
        Asserts.isTrue(userPrinciple==null,new BusinessException("-2","用户认证失败"));
        //2.判断角色 SHOP 条件里封装businessId USER角色异常结束 ADMIN 角色什么都不做
        Asserts.isTrue(userPrinciple.getRole()== Roles.USER,new BusinessException("-2","普通用户不能查询优惠券"));
        if (userPrinciple.getRole()==Roles.SHOP){
            //2.1 SHOP商家账号查询调用basic接口
            Integer adminId=userPrinciple.getId();
            AdminDTO adminDetail = basicApi.getAdminDetail(adminId);
            Asserts.isTrue(adminDetail==null,new BusinessException("-2","商家账号异常"));
            query.setBusinessId(adminDetail.getBusinessId());
        }
        //2.2 AdminPO商家账号详细数据不为空 补充条件bizId
        //3.调用数据层返回分页数据
        PageInfo<CouponPO> pageInfo= couponRepository.getCouponsByPage(query);
        PageResult<CouponVO> voPage=new PageResult<>();
        voPage.setPageNo(pageInfo.getPageNum());
        voPage.setPageSize(pageInfo.getPageSize());
        voPage.setTotal(pageInfo.getTotal());
        List<CouponVO> vos=null;
        if (CollectionUtils.isNotEmpty(pageInfo.getList())){
            vos=pageInfo.getList().stream().map(po->{
                CouponVO vo=new CouponVO();
                BeanUtils.copyProperties(po,vo);
                return vo;
            }).collect(Collectors.toList());
        }
        voPage.setObjects(vos);
        return voPage;
    }

    public void couponSave(CouponParam param) throws BusinessException, ParseException {
        //1.获取认证对象
        UserPrinciple userPrinciple = LbsSecurityContenxt.getLoginToken();
        Asserts.isTrue(userPrinciple==null,new BusinessException("-2","用户认证失败"));
        //2.只有商家账号可以新增
        Asserts.isTrue(userPrinciple.getRole()!=Roles.SHOP,new BusinessException("-2","新增优惠券权限不足"));
        //3.调用basic接口查询商家账户信息 为了补充优惠券表单数据
        AdminDTO adminDTO = basicApi.getAdminDetail(userPrinciple.getId());
        Asserts.isTrue(adminDTO==null,new BusinessException("-2","商家账号异常"));
        //4.封装CouponPO准备 写入到数据库
        CouponPO couponPO = assembleCouponPO(param,adminDTO.getBusinessId());
        //5.调用优惠券数据层写入到优惠券表格
        couponRepository.saveCoupon(couponPO);
        //6.写入优惠券发送总量的券码到 coupon_code 同步调用
        List<CouponCodePO> couponCodePOS=assembleCouponCodePos(couponPO);
        //7.执行批量写入
        couponCodeRepository.saveBatch(couponCodePOS);
        //8.远程调用stock服务同步优惠券库存(发送总量一样数据)
        initCouponStock(couponPO);
        //对redis库存做初始化 发行量就是redis当前存储优惠券的数字
        String couponStockKey = "coupon:stock:"+couponPO.getId();
        stringRedisTemplate.opsForValue().set(couponStockKey,param.getMaxUsageLimit().toString());
    }

    private void initCouponStock(CouponPO couponPO) throws BusinessException {
        CouponStockParam param = new CouponStockParam();
        param.setCouponId(couponPO.getId());
        param.setBusinessId(couponPO.getBusinessId());
        param.setNum(couponPO.getMaxUsageLimit());
        boolean result = stockApi.initCouponStock(param);
        Asserts.isTrue(!result,new BusinessException("-2","库存初始化失败"));

    }

    private List<CouponCodePO> assembleCouponCodePos(CouponPO couponPO) {
        Integer total = couponPO.getMaxUsageLimit();
        List<CouponCodePO> pos = new ArrayList<>();
        for (Integer i =0;i<total;i++){
            CouponCodePO couponCodePO = new CouponCodePO();
            couponCodePO.setBusinessId(couponPO.getBusinessId());
            couponCodePO.setCouponId(couponPO.getId());
            couponCodePO.setStatus(0);
            couponCodePO.setCreateAt(couponPO.getCreateAt());
            couponCodePO.setUpdateAt(couponPO.getUpdateAt());
            couponCodePO.setCouponCode(UUID.randomUUID().toString());
            pos.add(couponCodePO);
        }
        return pos;
    }

    private CouponPO assembleCouponPO(CouponParam param, Integer businessId) throws ParseException {
        //1.将表单参数 能拷贝的全拷贝
        CouponPO poParam=new CouponPO();
        BeanUtils.copyProperties(param,poParam);
        //2.单独填补一些不存在的数据
        //2.1优惠券所属商家
        poParam.setBusinessId(businessId);
        //2.2优惠券状态 待激活0,待使用1,过期2,作废3
        poParam.setStatus(1);
        //2.3优惠券管理状态 启用0 禁用1
        poParam.setEnableStatus(0);
        //3.优惠券数据库通用字段时间
        poParam.setCreateAt(new Date());
        poParam.setUpdateAt(poParam.getCreateAt());
        //4.前端为了方便 传递的startTime endTime时间是字符串 需要转换成Date类型
        //时间字符串格式 yyyy-MM-dd HH:mm:ss
        poParam.setStartDate(DateUtils.parseDate(param.getStartDate(),"yyyy-MM-dd HH:mm:ss"));
        poParam.setEndDate(DateUtils.parseDate(param.getEndDate(),"yyyy-MM-dd HH:mm:ss"));
        return poParam;
    }

    public CouponVO couponDetail(Integer id) {
        CouponBO bo=couponRepository.getCouponById(id);
        CouponVO vo=null;
        if (bo!=null){
            vo=new CouponVO();
            BeanUtils.copyProperties(bo,vo);
        }
        return vo;
    }

    public void saveUserReceiveCoupon(UserCouponsParam param) throws BusinessException {
        //1. 用户信息 消息中携带
        Integer userId = param.getUserId();
        log.info("userId={}",userId);
        //2. 查询优惠券详情
        Integer couponId = param.getCouponId();
        CouponBO couponBO = couponRepository.getCouponById(couponId);
        Asserts.isTrue(couponBO==null,new BusinessException("-2","没有优惠券"));
        //3. 查询活动详情 用户群体 在活动管理数据中
        Integer activityId = param.getActivityId();
        MarketingActivityBO activityBO = activityRepository.getActivitiesById(activityId);
        Asserts.isTrue(activityBO==null,new BusinessException("-3","没有活动"));
        Integer usageLimit = couponBO.getUsageLimit();
        //4.查询当前用户 userId 已经领取优惠 couponId 券记录TODO
        //SELECT * FROM user_receive_coupon where user_id=#{userId} and coupon_id=#{couponId}
        List<UserReceiveCouponBO> receiveBOS= userReceiveCouponRepo.receiveCouponList(userId,couponId);
        Asserts.isTrue(receiveBOS!=null&&receiveBOS.size()>=usageLimit,new BusinessException("-4","领多了"));
        //5.校验人群 用户--标签  人群--标签 活动--人群 basic
        if (org.springframework.util.CollectionUtils.isEmpty(receiveBOS)){
            //5.校验人群 用户--标签  人群--标签 活动--人群 basic
            String groupId = activityBO.getTargetCustomer();//用户人群名称
            //设计接口 RPC调用
            boolean isGroup=userApi.isUserGroup(userId,Integer.valueOf(groupId));
            Asserts.isTrue(!isGroup,new BusinessException("-5","不符合人群"));
        }
        //6.扣减库存 未必成功 RPC 调用 这步完成扣减 才是真正的领到了优惠券 利用couponId
        //update coupon_stock set num=num-1 where coupon_id=#{couponId} and num>0;
        boolean result = stockApi.updateCouponNum(couponId);
        Asserts.isTrue(!result,new BusinessException("-2","库存不足"));
        //7.领取优惠券code码,保证是可用的 status=0(未领取) 字符串 coupon_code
        //select * from coupon_code where coupon_id=#{} and status=0
        CouponCodeBO couponCodeBO=couponCodeRepository.getAvailableCouponCode(couponId);
        //8.记录领取 user_receive_coupon数据
        //补全bo
        param.setCouponCode(couponCodeBO.getCouponCode());
        UserReceiveCouponBO userReceiveCouponBO=new UserReceiveCouponBO();
        BeanUtils.copyProperties(param,userReceiveCouponBO);
        userReceiveCouponBO.setCouponName(couponBO.getCouponName());
        userReceiveCouponBO.setCouponType(couponBO.getCouponType());
        userReceiveCouponBO.setCouponValue(couponBO.getDiscountValue());
        userReceiveCouponBO.setStatus(0);
        userReceiveCouponBO.setCreateAt(new Date());
        //insert into user_receive_coupon (全字段) values(#{属性})
        userReceiveCouponRepo.saveUserReceiveCoupon(userReceiveCouponBO);
        //9.修改一下 领到的coupon_code status=1(已领取)
        //update coupon_code set status=1 where id=#{id};
        couponCodeRepository.updateReceivedById(couponCodeBO);

    }

    public BasePageList<UserReceiveCouponBO> userReceivePage(UserCouponQuery query) throws BusinessException {
        UserPrinciple userPrinciple = getUserPrinciple();
        query.setUserId(userPrinciple.getId());
        return userReceiveCouponRepo.userReceivePage(query);
    }
    private UserPrinciple getUserPrinciple() throws BusinessException {
        UserPrinciple userPrinciple = LbsSecurityContenxt.getLoginToken();
        if (userPrinciple == null){
            throw new BusinessException("101","用户认证解析失败");
        }
        return userPrinciple;
    }

    public UserReceiveCouponBO userReceiveDetail(UserCouponCodeQuery query) throws  BusinessException{
        //1.使用code某一个优惠券码 查询一个领取记录详情
        UserReceiveCouponBO bo = userReceiveCouponRepo.getUserReciveDetail(query.getCouponCode());
        Asserts.isTrue(bo==null,new BusinessException("-2","没有领取记录"));;
        //2.走缓存流程 存储的就是codeUrl code做key url做value
        String codeKey = "user:coupon:code:url:"+query.getCouponCode();
        String codeUrl = stringRedisTemplate.opsForValue().get(codeKey);
        if (StringUtils.isNotEmpty(codeUrl)){
            //非空 命中 封装返回
            bo.setCouponUrl(codeUrl);
        }else {
            //空 未命中 调用attach生成 携带http://www.baidu.com?code=券码
            AttachQRParam qrParam = new AttachQRParam();
            qrParam.setContent("http://www.baidu.com?code="+query.getCouponCode());
            //绑定当前优惠券 couponCodeId 500
            qrParam.setBusinessId(500);
            qrParam.setBusinessId(bo.getId());
            AttachQrDTO attachQrDTO = attachApi.generateQrCode(qrParam);
            String url = attachQrDTO.getUrl();
            //缓存填补数据
            stringRedisTemplate.opsForValue().set(codeKey,url);
            bo.setCouponUrl(url);
        }
        return bo;
    }

    public void locationReciveCoupon(UserCouponsParam message) throws BusinessException {
        //1.组织一下 领取优惠券 给用户发优惠券(新增领取记录) 完整数据 缺少couponId和activityId
        Integer shopId = message.getShopId();
        List<MarketingActivityBO> bos = activityRepository.getActivityByShopId(shopId);
        Asserts.isTrue(CollectionUtils.isEmpty(bos),new BusinessException("-2","没有活动"));
        log.info("店铺id{},绑定了活动:{}",message.getShopId(),bos);
        List<UserCouponsParam> params = new ArrayList<>();
        for (MarketingActivityBO bo : bos) {
            Integer couponId = bo.getRewardCouponId();
            Integer activityId = bo.getId();
            Integer recevieChannel = bo.getDeliveryChannel();
            UserCouponsParam userCouponsParam = new UserCouponsParam();
            userCouponsParam.setUserId(message.getUserId());
            userCouponsParam.setShopId(message.getShopId());
            userCouponsParam.setCouponId(couponId);
            userCouponsParam.setActivityId(activityId);
            userCouponsParam.setReceiveChannel(recevieChannel);
            params.add(userCouponsParam);
        }
        saveUserReceiveCoupon(params);
    }
    private void saveUserReceiveCoupon(List<UserCouponsParam> params) throws BusinessException {
        log.info("领取优惠券新增领取记录:{}",params);
        for (UserCouponsParam param : params) {
            saveUserReceiveCoupon(param);
        }
    }

    public List<Integer> getAvalicableIds() {
        return couponRepository.getAvailableCouponIds();
    }
}
